
# 从 collections 模块导入 OrderedDict 类，用于创建有序字典
from collections import OrderedDict
import os
# 导入 importlib.util 模块，用于动态导入模块
import importlib.util
# 导入 inspect 模块，用于获取对象的信息，如类、函数等
import inspect

from src.Core import PinBase
from src.Core import NodeBase
from src.Core import FunctionLibraryBase
from src.UI.UIInterfaces import IDataExporter
from src.UI.Widgets.PreferencesWindow import CategoryWidgetBase
from src.UI.Tool.Tool import ToolBase


class PackageBase(object):
    """
    描述一组可插入到编辑器中的模块的类。
    该类的实例将用于创建已注册的实体。

    Attributes:
        _FOO_LIBS (dict): 存储函数库实例的字典。
        _NODES (dict): 存储节点类的字典。
        _PINS (dict): 存储引脚类的字典。
        _TOOLS (OrderedDict): 存储工具类的有序字典。
        _PREFS_WIDGETS (OrderedDict): 存储首选项窗口部件类的有序字典。
        _EXPORTERS (OrderedDict): 存储数据导出器类的有序字典。
        _PinsInputWidgetFactory (function): 引脚输入部件工厂函数。
        _UINodesFactory (function): UI 节点工厂函数。
        _UIPinsFactory (function): UI 引脚工厂函数。
    """

    def __init__(self):
        """
        初始化 PackageBase 类的实例。
        初始化用于存储各种实体的字典和工厂函数。
        """
        super(PackageBase, self).__init__()
        # 存储函数库实例的字典
        self._FOO_LIBS = {}
        # 存储节点类的字典
        self._NODES = {}
        # 存储引脚类的字典
        self._PINS = {}
        # 存储工具类的有序字典
        self._TOOLS = OrderedDict()
        # 存储首选项窗口部件类的有序字典
        self._PREFS_WIDGETS = OrderedDict()
        # 存储数据导出器类的有序字典
        self._EXPORTERS = OrderedDict()

        # 引脚输入部件工厂函数
        self._PinsInputWidgetFactory = None
        # UI 节点工厂函数
        self._UINodesFactory = None
        # UI 引脚工厂函数
        self._UIPinsFactory = None

    def analyzePackage(self, packagePath):
        """
        分析指定路径下的包，加载包中的各种元素，如函数库、节点、引脚等。

        Args:
            packagePath (str): 包的路径。
        """

        def import_subclasses(directory, base_class):
            """
            动态导入指定目录下所有继承自 base_class 的子类。

            Args:
                directory (str): 目录路径。
                base_class (class): 基类。

            Returns:
                list: 包含所有找到的子类的列表。
            """
            subclasses = []
            # 遍历指定目录下的所有文件
            for filename in os.listdir(directory):
                if filename.endswith(".py") and not filename.startswith("__"):
                    # 构建模块名称
                    module_name = "PyFlow.Packages." + self.__class__.__name__ + "." + os.path.basename(
                        directory) + "." + filename[:-3]
                    file_path = os.path.join(directory, filename)
                    # 动态加载模块
                    spec = importlib.util.spec_from_file_location(module_name, file_path)
                    module = importlib.util.module_from_spec(spec)
                    spec.loader.exec_module(module)
                    # 遍历模块中的所有类
                    for name, obj in inspect.getmembers(module, inspect.isclass):
                        # 确保类是在当前模块中定义的，避免导入其他模块的类
                        # if inspect.getmodule(obj) == None or inspect.getmodule(obj) == module:
                        # 检查类是否是 base_class 的子类且不是 base_class 本身
                        if issubclass(obj, base_class) and obj is not base_class:
                            subclasses.append(obj)
            return subclasses

        def loadPackageElements(packagePath, element, elementDict, classType):
            """
            加载指定包路径下的特定元素。

            Args:
                packagePath (str): 包的路径。
                element (str): 元素名称，如 "FunctionLibraries"、"Nodes" 等。
                elementDict (dict): 用于存储加载的元素的字典。
                classType (class): 元素的类类型。
            """
            packageFolders = os.listdir(packagePath)
            if element in packageFolders:
                directory = os.path.join(packagePath, element)
                # 导入指定目录下的子类
                found_subclasses = import_subclasses(directory, classType)
                for subclass in found_subclasses:
                    if classType == FunctionLibraryBase:
                        # 若为函数库类，创建实例并存储到字典中
                        elementDict[subclass.__name__] = subclass(self.__class__.__name__, )
                    else:
                        # 否则，直接存储类到字典中
                        elementDict[subclass.__name__] = subclass

        # 加载包中的所有元素
        for element in [("FunctionLibraries", self._FOO_LIBS, FunctionLibraryBase),
                        ("Nodes", self._NODES, NodeBase),
                        ("Pins", self._PINS, PinBase),
                        ("Tools", self._TOOLS, ToolBase),
                        ("Exporters", self._EXPORTERS, IDataExporter),
                        ("PrefsWidgets", self._PREFS_WIDGETS, CategoryWidgetBase)]:
            loadPackageElements(packagePath, element[0], element[1], element[2])
        # 检查是否存在 Factories 目录
        if os.path.exists(os.path.join(packagePath, "Factories")):
            modPrefix = "PyFlow.Packages." + self.__class__.__name__ + ".Factories."
            # 检查是否存在 UIPinFactory.py 文件
            if os.path.exists(os.path.join(packagePath, "Factories", "UIPinFactory.py")):
                spec = importlib.util.spec_from_file_location(modPrefix + "UIPinFactory",
                                                              os.path.join(packagePath, "Factories", "UIPinFactory.py"))
                module = importlib.util.module_from_spec(spec)
                spec.loader.exec_module(module)
                # 设置 UI 引脚工厂函数
                self._UIPinsFactory = module.createUIPin
            # 检查是否存在 UINodeFactory.py 文件
            if os.path.exists(os.path.join(packagePath, "Factories", "UINodeFactory.py")):
                spec = importlib.util.spec_from_file_location(modPrefix + "UINodeFactory",
                                                              os.path.join(packagePath, "Factories",
                                                                           "UINodeFactory.py"))
                module = importlib.util.module_from_spec(spec)
                spec.loader.exec_module(module)
                # 设置 UI 节点工厂函数
                self._UINodesFactory = module.createUINode
            # 检查是否存在 PinInputWidgetFactory.py 文件
            if os.path.exists(os.path.join(packagePath, "Factories", "PinInputWidgetFactory.py")):
                spec = importlib.util.spec_from_file_location(modPrefix + "PinInputWidgetFactory",
                                                              os.path.join(packagePath, "Factories",
                                                                           "PinInputWidgetFactory.py"))
                module = importlib.util.module_from_spec(spec)
                spec.loader.exec_module(module)
                # 设置引脚输入部件工厂函数
                self._PinsInputWidgetFactory = module.getInputWidget

    def GetExporters(self):
        """
        获取已注册的编辑器数据导出器。

        Returns:
            dict(str, class): 包含数据导出器类的字典，键为类名。
        """
        return self._EXPORTERS

    def GetFunctionLibraries(self):
        """
        获取已注册的函数库实例。

        Returns:
            dict(str, object): 包含函数库实例的字典，键为类名。
        """
        return self._FOO_LIBS

    def GetNodeClasses(self):
        """
        获取已注册的节点类。

        Returns:
            dict(str, class): 包含节点类的字典，键为类名。
        """
        return self._NODES

    def GetPinClasses(self):
        """
        获取已注册的引脚类。

        Returns:
            dict(str, class): 包含引脚类的字典，键为类名。
        """
        return self._PINS

    def GetToolClasses(self):
        """
        获取已注册的工具类。

        Returns:
            dict(str, class): 包含工具类的字典，键为类名。
        """
        return self._TOOLS

    def PrefsWidgets(self):
        """
        获取已注册的首选项窗口部件类。

        Returns:
            dict(str, class): 包含首选项窗口部件类的字典，键为类名。
        """
        return self._PREFS_WIDGETS

    def UIPinsFactory(self):
        """
        获取已注册的 UI 引脚包装器工厂函数。

        Returns:
            function: UI 引脚包装器工厂函数。
        """
        return self._UIPinsFactory

    def UINodesFactory(self):
        """
        获取已注册的 UI 节点工厂函数。

        Returns:
            function: UI 节点工厂函数。
        """
        return self._UINodesFactory

    def PinsInputWidgetFactory(self):
        """
        获取已注册的引脚输入部件工厂函数。

        Returns:
            function: 引脚输入部件工厂函数。
        """
        return self._PinsInputWidgetFactory